unit uSocket;

interface

uses
  Windows,hxUDP,IdTCPClient,IdSocks,Dialogs,SysUtils,Forms;

type
  TSoitunSocketCategory = (scUDP,scTCP);
  TSoitunSocket = class
  public
    ReceiverID:Integer;
    MySocketCategory  :TSoitunSocketCategory;
    ReceiverLocalIP   :String;     
    ReceiverLocalPort :Integer;    
    ReceiverIP        :String;     
    ReceiverPort      :Integer;    
    LocalIP           :String;     
    LocalPort         :Integer;    
    IP                :String;     
    Port              :Integer;
    RemoteAdd:String;
    RemotePort:Integer;
    constructor Create(ID:Integer;ClientTCP:TIdTCPClient;ForTalking:Boolean=False);
    destructor Destroy;override;
    procedure SendBuffer(var Buf; Size: Integer; MustUseTCP: Boolean=False);
    procedure GetNATIPPort;
    procedure BeginGetHole;
    procedure KeepP2PSession;
  private
    IsForTalking:Boolean;
    ClientUDPSocket:ThxUDPSocket;
    ClientTCPSocket:TIdTCPClient;
    procedure FUdpServerDataRead(UDPSocket: ThxUDPSocket;const PeerInfo: TPeerInfo);
  end;

implementation

uses global,main,protocol;

procedure TSoitunSocket.KeepP2PSession;
var
  skType:char;
begin
  try
    GetNATIPPort;
    if (MySocketCategory=scUDP) then
    begin
      skType:=#128;
      ClientUDPSocket.SendBuf(skType,1,RemoteAdd,RemotePort);
    end;
  except
  end;
end;

//------------------------------------------------------------------------------
procedure TSoitunSocket.BeginGetHole;
var
  Buffer:Array[1..2048] of char;
  CBPleaseCallMe:TCBPleaseCallMe;
  StartTicket:Integer;
begin
  try
    if (MySocketCategory=scUDP) then Exit;

    StartTicket:=0;
    while StartTicket<100 do
    begin
      if (IP<>'') and (Port<>0) then break;
      Inc(StartTicket);
      Application.ProcessMessages;
      Sleep(30);
    end;

    CBPleaseCallMe.Receiver:=ReceiverID;
    CBPleaseCallMe.Sender:=Me.ID;
    CBPleaseCallMe.IP:=ReceiverIP;
    CBPleaseCallMe.Port:=ReceiverPort;
    CBPleaseCallMe.MyLocalIP:=LocalIP;
    CBPleaseCallMe.MyLocalPort:=LocalPort;
    CBPleaseCallMe.MyIP:=IP;
    CBPleaseCallMe.MyPort:=Port;

    try
      CBPleaseCallMe.UseIP:=ReceiverLocalIP;
      CBPleaseCallMe.UsePort:=ReceiverLocalPort;
      Buffer[1]:=skPleaseCallMe;
      CopyMemory(@Buffer[2],@CBPleaseCallMe,SizeOf(CBPleaseCallMe));
      ClientUDPSocket.SendBuf(Buffer,SizeOf(CBPleaseCallMe)+1,ReceiverLocalIP,ReceiverLocalPort);
      Sleep(50);
    except
    end;

    try
      CBPleaseCallMe.UseIP:=ReceiverIP;
      CBPleaseCallMe.UsePort:=ReceiverPort;
      Buffer[1]:=skPleaseCallMe;
      CopyMemory(@Buffer[2],@CBPleaseCallMe,SizeOf(CBPleaseCallMe));
      ClientUDPSocket.SendBuf(Buffer,SizeOf(CBPleaseCallMe)+1,ReceiverIP,ReceiverPort);
      Sleep(50);
    except
    end;

    CBPleaseCallMe.UseIP:=HostIP;
    CBPleaseCallMe.UsePort:=ServerPort;
    Buffer[1]:=skPleaseCallMe;
    CopyMemory(@Buffer[2],@CBPleaseCallMe,SizeOf(CBPleaseCallMe));
    ClientUDPSocket.SendBuf(Buffer,SizeOf(CBPleaseCallMe)+1,HostIP,ServerPort);

  except
  end;
end;

//------------------------------------------------------------------------------
procedure TSoitunSocket.SendBuffer(var Buf; Size: Integer; MustUseTCP: Boolean=False);
begin
  if (MySocketCategory=scTCP) or (MustUseTCP=True) then
    ClientTCPSocket.Socket.Send(Buf,Size)
  else
    ClientUDPSocket.SendBuf(Buf,Size,RemoteAdd,RemotePort);
end;

//------------------------------------------------------------------------------
procedure TSoitunSocket.GetNATIPPort;
var
  Buffer:Array[1..2048] of char;
  CBGetMyIPAndPort:TCBGetMyIPAndPort;
begin
  try
    if ReceiverID=0 then exit;

    CBGetMyIPAndPort.Sender:=Me.ID;
    CBGetMyIPAndPort.LocalIP:=LocalIP;
    CBGetMyIPAndPort.LocalPort:=LocalPort;
    CBGetMyIPAndPort.IP:='';
    CBGetMyIPAndPort.Port:=0;
    Buffer[1]:=skGetMyIPAndPort;
    CopyMemory(@Buffer[2],@CBGetMyIPAndPort,SizeOf(CBGetMyIPAndPort));
    ClientUDPSocket.SendBuf(Buffer,SizeOf(CBGetMyIPAndPort)+1,HostIP,ServerPort);
  except
  end;
end;

//------------------------------------------------------------------------------
constructor TSoitunSocket.Create(ID:Integer;ClientTCP:TIdTCPClient;ForTalking:Boolean=False);
begin
  ReceiverID        :=  ID;
  ClientTCPSocket   :=  ClientTCP;
  IsForTalking      :=  ForTalking;
  MySocketCategory  :=  scTCP;

  ReceiverLocalIP   :=  '';
  ReceiverLocalPort :=  0;
  ReceiverIP        :=  '';
  ReceiverPort      :=  0;
  LocalIP           :=  '';
  LocalPort         :=  0;
  IP                :=  '';
  Port              :=  0;
  RemoteAdd         :=  '';
  RemotePort        :=  0;

  try
    ClientUDPSocket:=ThxUDPSocket.Create;
    ClientUDPSocket.OnDataRead:=FUdpServerDataRead;
    ClientUDPSocket.Port:=0;

    if ProxyCategory=pcSocks5 then
    with ClientUDPSocket.ProxyInfo do
    begin
      Enabled:=True;
      Address:=ProxyAddress;
      Port:=ProxyPort;
      Username:=ProxyUsername;
      Password:=ProxyPassword;
    end;
    ClientUDPSocket.Active:=True;
    LocalIP:=GetHostIP('');
    LocalPort:=ClientUDPSocket.Port;
    GetNATIPPort;

    MySockets.Add(Self);
  except
  end;
end;

//------------------------------------------------------------------------------
destructor TSoitunSocket.Destroy;
begin
  try
    ClientUDPSocket.Active:=False;
  finally
    ClientUDPSocket.Free;
  end;
  MySockets.Remove(Self);
end;

//------------------------------------------------------------------------------
procedure TSoitunSocket.FUdpServerDataRead(UDPSocket: ThxUDPSocket; const PeerInfo: TPeerInfo);
var
  skType:Char;
  ReceiverBuffer:array[1..2048] of char;
  Buffer:array[1..2048] of char;
  
  CBGetMyIPAndPort:TCBGetMyIPAndPort;
  CBPleaseCallMe:TCBPleaseCallMe;
  CBCallSomeone:TCBCallSomeone;
  CBP2PResponse,CBP2PResponse2:TCBP2PResponse;
  CBCalled:TCBCalled;
  CBAudio:TCBAudio;
  CBVideo:TCBVideo;
  CBSendFilePackage:TCBSendFilePackage;
  CBSendFileResult:TCBSendFileResult;
  CBInputing:TCBInputing;
  CBMessage:TCBMessage;
  CBReturnMessage:TCBReturnMessage;
  CBBeginTalk:TCBBeginTalk;

  PortChanged:Boolean;
begin
  if UDPSocket.RecvBuf(ReceiverBuffer, SizeOf(ReceiverBuffer), PeerInfo.PeerIP, PeerInfo.PeerPort) then
  begin
    skType:=ReceiverBuffer[1];

    {**************************************************************************}
    if skType = skGetMyIPAndPort then
    begin
      CopyMemory(@CBGetMyIPAndPort,@ReceiverBuffer[2],SizeOf(CBGetMyIPAndPort));
      if CBGetMyIPAndPort.Sender=Me.ID then
      begin
        IP:=CBGetMyIPAndPort.IP;
        PortChanged:=False;
        if Port<>CBGetMyIPAndPort.Port then
        begin
          Port:=CBGetMyIPAndPort.Port;
          PortChanged:=True;
        end;
        if IsForTalking and PortChanged then
        begin
          CBBeginTalk.Receiver:=ReceiverID;
          CBBeginTalk.Sender:=Me.ID;
          CBBeginTalk.LocalIP:=LocalIP;
          CBBeginTalk.LocalPort:=LocalPort;
          CBBeginTalk.IP:=IP;
          CBBeginTalk.Port:=Port;
          Buffer[1]:=skBeginTalk;
          CopyMemory(@Buffer[2],@CBBeginTalk,SizeOf(CBBeginTalk));
          SendBuffer(Buffer,SizeOf(CBBeginTalk)+1,True);
        end;
      end;
      Exit;
    end;

    {**************************************************************************}
    if skType = skPleaseCallMe then
    begin
      CopyMemory(@CBPleaseCallMe,@ReceiverBuffer[2],SizeOf(CBPleaseCallMe));
      if CBPleaseCallMe.Sender<>ReceiverID then Exit;

      if ((PeerInfo.PeerIP<>HostIP) and (PeerInfo.PeerPort<>ServerPort)) then
      begin
        CBP2PResponse2.Receiver:=ReceiverID;
        CBP2PResponse2.Sender:=Me.ID;
        CBP2PResponse2.IP:=ReceiverIP;
        CBP2PResponse2.Port:=ReceiverPort;
        CBP2PResponse2.PeerIP:=CBPleaseCallMe.UseIP;
        CBP2PResponse2.PeerPort:=CBPleaseCallMe.UsePort;
        CBP2PResponse2.UseIP:=PeerInfo.PeerIP;
        CBP2PResponse2.UsePort:=PeerInfo.PeerPort;
        Buffer[1]:=skP2PResponse;
        CopyMemory(@Buffer[2],@CBP2PResponse,SizeOf(CBP2PResponse));
        ClientUDPSocket.SendBuf(Buffer,SizeOf(CBP2PResponse)+1,PeerInfo.PeerIP,PeerInfo.PeerPort);
        Sleep(50);
              
        CBP2PResponse2.UseIP:=HostIP;
        CBP2PResponse2.UsePort:=ServerPort;
        Buffer[1]:=skP2PResponse;
        CopyMemory(@Buffer[2],@CBP2PResponse,SizeOf(CBP2PResponse));
        ClientUDPSocket.SendBuf(Buffer,SizeOf(CBP2PResponse)+1,HostIP,ServerPort);
        Exit;
      end;

      ReceiverLocalIP:=CBPleaseCallMe.MyLocalIP;
      ReceiverLocalPort:=CBPleaseCallMe.MyLocalPort;
      ReceiverIP:=CBPleaseCallMe.MyIP;
      ReceiverPort:=CBPleaseCallMe.MyPort;

      CBCallSomeone.Receiver:=ReceiverID;
      CBCallSomeone.Sender:=Me.ID;
      CBCallSomeone.IP:=ReceiverLocalIP;
      CBCallSomeone.Port:=ReceiverLocalPort;
      Buffer[1]:=skCallSomeone;
      CopyMemory(@Buffer[2],@CBCallSomeone,SizeOf(CBCallSomeone));
      try
        ClientUDPSocket.SendBuf(Buffer,SizeOf(CBCallSomeone)+1,ReceiverLocalIP,ReceiverLocalPort);
        Sleep(50);
      except
      end;

      CBCallSomeone.Receiver:=ReceiverID;
      CBCallSomeone.Sender:=Me.ID;
      CBCallSomeone.IP:=ReceiverIP;
      CBCallSomeone.Port:=ReceiverPort;
      Buffer[1]:=skCallSomeone;
      CopyMemory(@Buffer[2],@CBCallSomeone,SizeOf(CBCallSomeone));
      try
        ClientUDPSocket.SendBuf(Buffer,SizeOf(CBCallSomeone)+1,ReceiverIP,ReceiverPort);
        Sleep(50);
      except
      end;

      CBCalled.Receiver:=ReceiverID;
      CBCalled.Sender:=Me.ID;
      CBCalled.IP:=ReceiverIP;
      CBCalled.Port:=ReceiverPort;
      Buffer[1]:=skCalled;
      CopyMemory(@Buffer[2],@CBCalled,SizeOf(CBCalled));
      ClientUDPSocket.SendBuf(Buffer,SizeOf(CBCalled)+1,PeerInfo.PeerIP,PeerInfo.PeerPort);
      Sleep(50);
      ClientUDPSocket.SendBuf(Buffer,SizeOf(CBCalled)+1,HostIP,ServerPort);

      Exit;
    end;

    {**************************************************************************}
    if skType = skCalled then
    begin
      CopyMemory(@CBCalled,@ReceiverBuffer[2],SizeOf(CBCalled));
      if CBCalled.Sender<>ReceiverID then Exit;

      CBCallSomeone.Receiver:=ReceiverID;
      CBCallSomeone.Sender:=Me.ID;
      CBCallSomeone.IP:=ReceiverLocalIP;
      CBCallSomeone.Port:=ReceiverLocalPort;
      Buffer[1]:=skCallSomeone;
      CopyMemory(@Buffer[2],@CBCallSomeone,SizeOf(CBCallSomeone));
      try
        ClientUDPSocket.SendBuf(Buffer,SizeOf(CBCallSomeone)+1,ReceiverLocalIP,ReceiverLocalPort);
        Sleep(50);
      except
      end;

      CBCallSomeone.Receiver:=ReceiverID;
      CBCallSomeone.Sender:=Me.ID;
      CBCallSomeone.IP:=ReceiverIP;
      CBCallSomeone.Port:=ReceiverPort;
      Buffer[1]:=skCallSomeone;
      CopyMemory(@Buffer[2],@CBCallSomeone,SizeOf(CBCallSomeone));
      ClientUDPSocket.SendBuf(Buffer,SizeOf(CBCallSomeone)+1,ReceiverIP,ReceiverPort);
      Exit;
   end;

    {**************************************************************************}
    if skType = skCallSomeone then
    begin
      CopyMemory(@CBCallSomeone,@ReceiverBuffer[2],SizeOf(CBCallSomeone));
      if CBCallSomeone.Sender<>ReceiverID then Exit;

      CBP2PResponse.Receiver:=ReceiverID;
      CBP2PResponse.Sender:=Me.ID;
      CBP2PResponse.IP:=ReceiverIP;
      CBP2PResponse.Port:=ReceiverPort;
      CBP2PResponse.PeerIP:=CBCallSomeone.IP;
      CBP2PResponse.PeerPort:=CBCallSomeone.Port;
      CBP2PResponse.UseIP:=PeerInfo.PeerIP;
      CBP2PResponse.UsePort:=PeerInfo.PeerPort;
      Buffer[1]:=skP2PResponse;
      CopyMemory(@Buffer[2],@CBP2PResponse,SizeOf(CBP2PResponse));
      try
        ClientUDPSocket.SendBuf(Buffer,SizeOf(CBP2PResponse)+1,PeerInfo.PeerIP,PeerInfo.PeerPort);
        Sleep(50);
      except
      end;

      CBP2PResponse.UseIP:=HostIP;
      CBP2PResponse.UsePort:=ServerPort;
      Buffer[1]:=skP2PResponse;
      CopyMemory(@Buffer[2],@CBP2PResponse,SizeOf(CBP2PResponse));
      ClientUDPSocket.SendBuf(Buffer,SizeOf(CBP2PResponse)+1,HostIP,ServerPort);
      Exit;
    end;

    {**************************************************************************}
    if skType = skP2PResponse then
    begin
      CopyMemory(@CBP2PResponse,@ReceiverBuffer[2],SizeOf(CBP2PResponse));
      if CBP2PResponse.Sender<>ReceiverID then Exit;
      if MySocketCategory=scUDP then Exit;
      
      MySocketCategory:=scUDP;
      RemoteAdd:=CBP2PResponse.PeerIP;
      RemotePort:=CBP2PResponse.PeerPort;

      if ((PeerInfo.PeerIP<>HostIP) and (PeerInfo.PeerPort<>ServerPort)) then
      begin
        CBP2PResponse2.Receiver:=ReceiverID;
        CBP2PResponse2.Sender:=Me.ID;
        CBP2PResponse2.IP:=ReceiverIP;
        CBP2PResponse2.Port:=ReceiverPort;
        CBP2PResponse2.PeerIP:=CBP2PResponse.UseIP;
        CBP2PResponse2.PeerPort:=CBP2PResponse.UsePort;
        CBP2PResponse2.UseIP:=PeerInfo.PeerIP;
        CBP2PResponse2.UsePort:=PeerInfo.PeerPort;
        Buffer[1]:=skP2PResponse;
        CopyMemory(@Buffer[2],@CBP2PResponse,SizeOf(CBP2PResponse));
        ClientUDPSocket.SendBuf(Buffer,SizeOf(CBP2PResponse)+1,PeerInfo.PeerIP,PeerInfo.PeerPort);        
        Sleep(50);        
        ClientUDPSocket.SendBuf(Buffer,SizeOf(CBP2PResponse)+1,HostIP,ServerPort);
      end;
       
      Exit;
    end;
    
    {************************************************************************}
    if skType = skInputing then
    begin
      CopyMemory(@CBInputing,@ReceiverBuffer[2],SizeOf(CBInputing));
      frmMain.ProcessCBInputing(CBInputing);
      Exit;
    end;

    {************************************************************************}
    if skType = skMessage then
    begin
      CopyMemory(@CBMessage,@ReceiverBuffer[2],SizeOf(CBMessage));
      frmMain.ProcessCBMessage(CBMessage);
      Exit;
    end;

    {************************************************************************}
    if skType = skReturnMessage then
    begin
      CopyMemory(@CBReturnMessage,@ReceiverBuffer[2],SizeOf(CBReturnMessage));
      frmMain.ProcessCBReturnMessage(CBReturnMessage);
      Exit;
    end;

    {**************************************************************************}
    if skType = skAudio then
    begin
      CopyMemory(@CBAudio,@ReceiverBuffer[2],SizeOf(CBAudio));
      frmMain.ProcessCBAudio(CBAudio);
      Exit;
    end;

    {**************************************************************************}
    if skType = skVideo then
    begin
      CopyMemory(@CBVideo,@ReceiverBuffer[2],SizeOf(CBVideo));
      frmMain.ProcessCBVideo(CBVideo);
      Exit;
    end;
    
    {**************************************************************************}
    if skType = skSendFilePackage then
    begin
      CopyMemory(@CBSendFilePackage,@ReceiverBuffer[2],SizeOf(CBSendFilePackage));
      frmMain.ProcessCBSendFilePackage(CBSendFilePackage);
      Exit;
    end;
    
    {************************************************************************}
    if skType = skSendFileResult then 
    begin
      CopyMemory(@CBSendFileResult,@ReceiverBuffer[2],SizeOf(CBSendFileResult));
      frmMain.ProcessCBSendFileResult(CBSendFileResult);
      Exit;
    end;
    
  end;
end;

end.
